package com.mall.b2c.service.impl;

import static com.mall.b2c.Constants.CAPTCHA_EXPIRED_TIME;
import static com.mall.b2c.Constants.USERNAME_PREFIX;
import static com.mall.b2c.util.SerialNumberUtil.RECHARGE_SN_PREFIX;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import javax.annotation.Resource;
import javax.persistence.LockModeType;

import com.mall.b2c.enums.IntergralLogType;
import com.mall.b2c.service.IntergralService;
import net.sf.json.JSONObject;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.mall.b2c.Setting;
import com.mall.b2c.bean.MemberBean;
import com.mall.b2c.dao.MemberDao;
import com.mall.b2c.domain.goods.GoodsDomain;
import com.mall.b2c.domain.member.MemberDomain;
import com.mall.b2c.domain.member.PaymentDetailDomain;
import com.mall.b2c.enums.DetailType;
import com.mall.b2c.service.GoodsService;
import com.mall.b2c.service.MemberService;
import com.mall.b2c.service.PaymentDetailService;
import com.mall.b2c.util.SerialNumberUtil;
import com.mall.b2c.util.SettingUtils;
import com.mall.common.bean.Pager;
import com.mall.common.domain.AreaDomain;
import com.mall.common.domain.component.PaymentTypeDomain;
import com.mall.common.enums.MessageType;
import com.mall.common.exception.NoticeException;
import com.mall.common.hibernate.Finder;
import com.mall.common.service.AreaService;
import com.mall.common.service.MailService;
import com.mall.common.service.MessageSendService;
import com.mall.common.service.impl.BaseServiceImpl;
import com.mall.common.util.CommonUtil;
import com.mall.common.util.PasswordUtil;
import com.mall.common.util.VerifyUtil;


/**
 * Service实现类 - 会员
 */
@Service
@Transactional
public class MemberServiceImpl extends BaseServiceImpl<MemberDomain, Long> implements
	MemberService {
	@Resource private MemberDao memberDao;
	@Resource private MailService mailService;
	@Resource private MessageSendService messageSendService;
	@Resource private AreaService areaService;
	@Resource private GoodsService goodsService;
	@Resource private PaymentDetailService paymentDetailService;
	@Resource private IntergralService intergralService;
	@Resource
	public void setBaseDao(MemberDao memberDao) {
		super.setBaseDao(memberDao);
	}

	@Override
	public boolean isExistMobile(String mobile, Long id) {
		Finder finder = Finder.create("from MemberDomain bean where bean.mobile =:mobile");
		if(id != null){
			finder.append(" and bean.id != :id");
		}
		finder.setParam("mobile", mobile);
		return !finder.findHQLList().isEmpty();
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED)
	public MemberBean register(String mobile, String password, String ip, Long inviterId) {
		if(isExist("mobile", mobile)){
			throw new NoticeException("手机号码已被使用");
		}
		String error = PasswordUtil.validPwd(password);
		if(StringUtils.isNotBlank(error)){
			throw new NoticeException(error);
		}
		MemberDomain member = new MemberDomain();
		synchronized(this) {
			member.setUsername(mobile);//USERNAME_PREFIX + Long.toHexString(System.currentTimeMillis())
		}
		MemberDomain inviter = null;
		if (inviterId != null) {
			inviter = find(inviterId);
		}
		if (inviter != null) {
			memberDao.lock(inviter, LockModeType.PESSIMISTIC_WRITE);
			inviter.setPoint(inviter.getPoint() + 10);
			update(inviter);
			intergralService.alterations(inviter.getId(), 10, IntergralLogType.addnum, "邀请好友【" + member.getUsername() + "】注册赠送！");
			member.setInviter(inviter);
		}
		member.setPassword(DigestUtils.md5Hex(password));
		member.setRegisterIp(ip);
		member.setMobile(mobile);
		persist(member);
		intergralService.saveIntergralDomain(member);
		return new MemberBean(member);
	}
	
	@Override
	public MemberBean login(String username, String password, String loginIP) {
		if(StringUtils.isBlank(username) || StringUtils.isBlank(password)){
			throw new NoticeException("您输入的密码和账户名不匹配，请重新输入");
		}
		MemberDomain member = getMemberByRegex(username);
		if(member == null){
			throw new NoticeException("您输入的密码和账户名不匹配，请重新输入");
		}
		if(member.getIsAccountEnabled() == null
				|| !member.getIsAccountEnabled()){
			throw new NoticeException("当前帐号不可用，请联系客服");
		}
		Date today = new Date();
		if(member.getIsAccountLocked()!= null 
				&& member.getIsAccountLocked()
				&& member.getLockedDate() != null
				&& DateUtils.addMinutes(member.getLockedDate(), SettingUtils.get().getLoginFailureLockTime()).compareTo(today) > 0){
			throw new NoticeException("该账户已被锁定，请稍后再试");
		}
		if(!DigestUtils.md5Hex(password).equals(member.getPassword())){
			throw new NoticeException("您输入的密码和账户名不匹配，请重新输入");
		}
		member.setLoginDate(today);
		member.setLoginIp(loginIP);
		member.setLoginFailureCount(0);
		update(member);
		return new MemberBean(member);
	}
	
	@Override
	@Transactional(propagation=Propagation.REQUIRED)
	public void loginFail(MemberDomain member) {
		if(member == null || member.getId() == null){
			return;
		}
		Integer loginFailCount = member.getLoginFailureCount();
		if(loginFailCount == null){
			loginFailCount = 1;
		}else{
			loginFailCount++;
		}
		member.setLoginFailureCount(loginFailCount);
		Setting setting = SettingUtils.get();
		if(loginFailCount >= setting.getLoginFailureLockCount()
				&& setting.getIsLoginFailureLock()){
			member.setLockedDate(new Date());
			member.setIsAccountLocked(true);
		}
		update(member);
	}

	@Override
	public boolean isExistEamil(String email, Long id) {
		Finder finder = Finder.create("from MemberDomain bean where bean.email =:email");
		if(id != null){
			finder.append(" and bean.id != :id");
		}
		finder.setParam("email", email);
		return !finder.findHQLList().isEmpty();
	}

	@Override
	public MemberDomain getMemberByRegex(String inputValue) {
		MemberDomain member = null;
		if(VerifyUtil.verifyEmail(inputValue, false)) {//邮箱
			member = this.get("email", inputValue);
		} else if(VerifyUtil.verifyMobile(inputValue, false)) {//手机
			member = this.get("mobile", inputValue);
		} else {
			member = this.get("username", inputValue);
		}
		return member;
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED, noRollbackFor=NoticeException.class)
	public JSONObject sendMobileCaptcha(String mobile, String ip, Boolean isCaptcha, Long memberId, int type) {
		String templateName = null;
		MessageType messageType = null;
		MemberDomain member = null;
		switch(type){
			case 0 :
				if (isExist("mobile", mobile)) {
					throw new NoticeException("手机号码已被使用！");
				}
				templateName = "smsRegister";
				messageType = MessageType.mobileVerify;
				break;
			case 1 :
				if (isExist("mobile", mobile)) {
					throw new NoticeException("手机号码已被绑定！");
				}
				templateName = "smsBind";
				messageType = MessageType.mobileVerify;
				break;
			case 2 :
				if(memberId == null){
					throw new NoticeException("会员不存在！");
				}
				member = find(memberId);
				if(member == null){
					throw new NoticeException("会员不存在！");
				}
				if(member.isMobileCodeValid()){
					throw new NoticeException("短信验证码未过期，可以继续使用！");
				}
				mobile = member.getMobile();
				templateName = "smsValid";
				messageType = MessageType.mobileVerify;
				break;
		}
		if(StringUtils.isBlank(mobile)){
			throw new NoticeException("手机号码错误！");
		}
		
		String validateCode = CommonUtil.getRandomNum(6);
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("key", validateCode);
		
		Boolean sendStatus = messageSendService.sendMessageTemplateName(mobile, ip, isCaptcha, templateName, map, null, messageType);
		if(!sendStatus){
			throw new NoticeException("发送短信失败!");
		}
		if(member != null){
			JSONObject codeInfo = new JSONObject();
			codeInfo.put("code", validateCode);
			codeInfo.put("expired", System.currentTimeMillis() + CAPTCHA_EXPIRED_TIME);
			member.setSmsVerifyCode(codeInfo.toString());
			update(member);
		}
		
		JSONObject jObj = new JSONObject();
		jObj.put("validateCode", validateCode);
		jObj.put("expired", CAPTCHA_EXPIRED_TIME);
		return jObj;
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED)
	public String sendEmail(String email, Long memberId, int type) {
		String templateName = null;
		MemberDomain member = null;
		switch(type){
			case 1 :
				if(!VerifyUtil.verifyEmail(email, false)){
					throw new NoticeException("电子邮箱错误！");
				}
				if(isExist("email", email)){
					throw new NoticeException("该邮件已被使用");
				}
				if(memberId == null){
					throw new NoticeException("会员不存在！");
				}
				member = find(memberId);
				if(member == null){
					throw new NoticeException("会员不存在！");
				}
				templateName = "mailBind";
				break;
			case 2 :
				if(!VerifyUtil.verifyEmail(email, false)){
					throw new NoticeException("电子邮箱错误！");
				}
				member = getMemberByRegex(email);
				if(member == null){
					throw new NoticeException("帐号不存在");
				}
				templateName = "mailRecovery";
				break;
			case 3 :
				if(memberId == null){
					throw new NoticeException("会员不存在！");
				}
				member = find(memberId);
				if(member == null){
					throw new NoticeException("会员不存在！");
				}
				email = member.getEmail();
				if(!VerifyUtil.verifyEmail(email, false)){
					throw new NoticeException("电子邮箱错误！");
				}
				templateName = "mailValid";
		}
		String code = CommonUtil.getUUID();
		Map<String, Object> params = new HashMap<String, Object>();
		if(member != null){
			member.addEmailVerifyCode(type, VerifyUtil.createEmailVerifyCodeKey(email, code));
			update(member);
			params.put("username", member.getUsername());
		}
		Setting setting = SettingUtils.get();
		params.put("shopUrl", setting.getUrlBuff(true));
		params.put("shopName", setting.getName());
		params.put("code", code);
		String title = setting.getName() + "邮箱验证";
		boolean flag = mailService.sendMail(templateName, email, title, params, SettingUtils.get());
		if(!flag){
			throw new NoticeException("邮件发送失败!");
		}
		return code;
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED)
	public MemberDomain modifyEmail(String code) {
		Finder finder = Finder.create("from MemberDomain bean where bean.emailVerifyCode like '%\"1\":\"").append(code).
				append(VerifyUtil.VERIFY_EMAIL_CODE_KEY_SEPARATOR).append("%").
				append(VerifyUtil.VERIFY_EMAIL_CODE_KEY_SEPARATOR).append("%@%\"%'");
		Object obj = finder.findUniqueByHQL();
		if(obj == null){
			throw new NoticeException("邮件已过期");
		}
		MemberDomain member = (MemberDomain) obj;
		memberDao.lock(member, LockModeType.PESSIMISTIC_WRITE);
		String error = member.verifyEmailCode(1, code);
		if(StringUtils.isNotBlank(error)){
			throw new NoticeException(error);
		}
		String email = VerifyUtil.getEmailFromVerifyCodeKey(member.getEmailVerifyCode(1));
		member.setEmail(email);
		member.removeEmailVerifyCode(1);
		memberDao.merge(member);
		return member;
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED)
	public void modifyMobile(String mobile, Long memberId) {
		if(isExist("mobile", mobile)){
			throw new NoticeException("该手机号码已被使用");
		}
		MemberDomain member = find(memberId);
		if(member == null){
			throw new NoticeException("会员不存在！");
		}
		memberDao.lock(member, LockModeType.PESSIMISTIC_WRITE);
		member.setMobile(mobile);
		update(member);
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED)
	public void modifyPwd(Long id, String oldPassword, String newPassword) {
		String error = PasswordUtil.validPwd(newPassword);
		if(StringUtils.isNotBlank(error)){
			throw new NoticeException(error);
		}
		MemberDomain member = find(id);
		if(member == null){
			throw new NoticeException("帐号不存在");
		}
		if(StringUtils.isBlank(oldPassword)
						|| !DigestUtils.md5Hex(oldPassword).equals(member.getPassword())){
			throw new NoticeException("原始密码不正确");
		}
		member.setPassword(DigestUtils.md5Hex(newPassword));
		update(member);
	}

	@Override
	public void resetPwd(Long id, String password) {
		String error = PasswordUtil.validPwd(password);
		if(StringUtils.isNotBlank(error)){
			throw new NoticeException(error);
		}
		MemberDomain member = find(id);
		if(member == null){
			throw new NoticeException("帐号不存在");
		}
		member.setPassword(DigestUtils.md5Hex(password));
		member.setEmailVerifyCode(null);
		update(member);
	}

	@Override
	public MemberBean recovery(String code) {
		Finder finder = Finder.create("from MemberDomain bean where bean.emailVerifyCode like '%\"2\":\"").append(code).
				append(VerifyUtil.VERIFY_EMAIL_CODE_KEY_SEPARATOR).append("%").
				append(VerifyUtil.VERIFY_EMAIL_CODE_KEY_SEPARATOR).append("%@%\"%'");
		Object obj = finder.findUniqueByHQL();
		if(obj == null){
			throw new NoticeException("邮件已过期");
		}
		MemberDomain member = (MemberDomain) obj;
		String error = member.verifyEmailCode(2, code);
		if(StringUtils.isNotBlank(error)){
			throw new NoticeException(error);
		}
		return new MemberBean(member);
	}

	@Override
	public Pager getFavoriteGoods(Long memberId, Integer pageNumber) {
		Pager pager = new Pager();
		pager.setPageSize(15);
		if(pageNumber != null && pageNumber > 0){
			pager.setPageNumber(pageNumber);
		}
		Finder finder = Finder.create("select new com.mall.b2c.bean.GoodsBean(bean, m) from MemberDomain m join m.favoriteGoods bean where m.id=:memberId");
		finder.setParam("memberId", memberId);
		return findByPagerForHessian(pager, finder);
	}

	@Override
	public void addFavoriteGoods(Long[] goodsIds, Long memberId) {
		if(memberId == null){
			throw new NoticeException("会员不存在");
		}
		if(goodsIds == null){
			throw new NoticeException("商品不存在");
		}
		MemberDomain member = find(memberId);
		if(member == null){
			throw new NoticeException("会员不存在");
		}
		List<GoodsDomain> goodsList = goodsService.get(goodsIds);
		if(goodsList == null){
			throw new NoticeException("商品不存在");
		}
		Set<GoodsDomain> goodsSet = member.getFavoriteGoods();
		int favoriteGoodsCount = goodsSet.size();
		for(GoodsDomain goods : goodsList) {
			if(goodsSet.contains(goods)){
				continue;
			}
			goodsSet.add(goods);
		}
		if(goodsSet.size() == favoriteGoodsCount) {
			throw new NoticeException("商品已收藏");
		}
		member.setFavoriteGoods(goodsSet);
		update(member);
	}

	@Override
	public void deleteFavoriteGoods(Long[] goodsIds, Long memberId) {
		if(memberId == null){
			throw new NoticeException("会员不存在");
		}
		if(goodsIds == null){
			throw new NoticeException("商品不存在");
		}
		MemberDomain member = find(memberId);
		if(member == null){
			throw new NoticeException("会员不存在");
		}
		Set<GoodsDomain> goodsSet = member.getFavoriteGoods();
		List<GoodsDomain> list = goodsService.get(goodsIds);
		for(GoodsDomain goods : list){
			if(!goodsSet.contains(goods)){
				continue;
			}
			goodsSet.remove(goods);
		}
		member.setFavoriteGoods(goodsSet);
		update(member);
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED)
	public void rechargeWithCard(Long memberId, BigDecimal money, Long id) {

		MemberDomain member = memberDao.find(memberId);
		memberDao.lock(member, LockModeType.PESSIMISTIC_WRITE);
		PaymentDetailDomain paymentDetail = new PaymentDetailDomain();
		paymentDetail.setPaymentNo(SerialNumberUtil.buildPaymentSn());
		paymentDetail.setDetailType(DetailType.recharge);
		paymentDetail.setAmount(money);
		BigDecimal deposit = member.getDeposit();
		paymentDetail.setBalance(member.getDeposit().add(money));
		paymentDetail.setOperator(member.getUsername());
		paymentDetail.setMember(member);
		paymentDetail.setPaymentTypeName("卡密充值");
		paymentDetail.setTradeNo(id + "");
		paymentDetailService.persist(paymentDetail);
		member.setDeposit(paymentDetail.getBalance());
		update(member);

	}


	@Override
	@Transactional(propagation = Propagation.REQUIRED)
	public boolean recharge(String tradeNo, PaymentTypeDomain paymentType,
			BigDecimal amount, String dealId, Long memberId) {
		MemberDomain member = memberDao.find(memberId);
		if(member == null){
			return false;
		}
		memberDao.lock(member, LockModeType.PESSIMISTIC_WRITE);
		if(StringUtils.isNotBlank(dealId) && paymentDetailService.isExist("dealId", dealId)){
			return true;
		}
		BigDecimal deposit = member.getDeposit();
		if(amount != null && amount.signum() > 0){//充值金额大于0
			PaymentDetailDomain paymentDetail = new PaymentDetailDomain();
			paymentDetail.setPaymentNo(SerialNumberUtil.buildPaymentSn());
			paymentDetail.setDetailType(DetailType.recharge);
			paymentDetail.setAmount(amount);
			paymentDetail.setBalance(deposit.add(amount));
			paymentDetail.setOperator(member.getUsername());
			paymentDetail.setMember(member);
			paymentDetail.setTradeNo(tradeNo);
			if(paymentType != null){
				paymentDetail.setPaymentTypeName(paymentType.getName());
			}
			if(tradeNo.startsWith(RECHARGE_SN_PREFIX)){
				paymentDetail.setDealId(dealId);
			}
			paymentDetailService.persist(paymentDetail);
			member.setDeposit(paymentDetail.getBalance());
			update(member);
			return true;
		}
		return false;
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED)
	public void saveMember(MemberBean member) {
		if(member.getId() == null){
			throw new NoticeException("会员不存在");
		}
		MemberDomain bean = find(member.getId());
		if(bean == null){
			throw new NoticeException("会员不存在");
		}
		bean.setName(member.getName());
		bean.setSex(member.getSex());
		bean.setBirthday(member.getBirthday());
		if(StringUtils.isBlank(member.getAreaTree())){
			bean.setArea(null);
		}else{
			String[] areaIds = member.getAreaTree().split(",");
			if(!Pattern.matches("^\\d+$", areaIds[areaIds.length - 1])){
				throw new NoticeException("地区不存在");
			}
			AreaDomain area = areaService.find(Long.valueOf(areaIds[areaIds.length - 1]));
			if(area == null){
				throw new NoticeException("地区不存在");
			}
			bean.setArea(area);
		}
		bean.setAddress(member.getAddress());
		bean.setPhone(member.getPhone());
		update(bean);
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED)
	public void disable(Long[] ids) {
		List<MemberDomain> list = get(ids);
		for(MemberDomain member : list){
			member.setIsAccountEnabled(false);
			update(member);
		}
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED)
	public void enable(Long[] ids) {
		List<MemberDomain> list = get(ids);
		for(MemberDomain member : list){
			member.setIsAccountEnabled(true);
			update(member);
		}
	}

}
